﻿using Kele.Activity.Core;
using Kele.Activity.Models;
using Kele.Activity.Web.Controllers;
using Kele.Activity.Web.Controllers.V1;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.IdentityModel.Tokens;
using Senparc.CO2NET.Extensions;
using Senparc.CO2NET.Helpers;
using Senparc.Weixin;
using Senparc.Weixin.MP;
using Senparc.Weixin.MP.AdvancedAPIs;
using Senparc.Weixin.MP.AdvancedAPIs.OAuth;
using Senparc.Weixin.MP.Helpers;
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using WxConfig = Senparc.Weixin.Config;

namespace Kele.Activity.Auth.Controllers
{

    [ApiVersionNeutral]
    [Route("api/account")]
    public class AccountController : SiteApiController
    {
        //第一步
        //PC版通过扫描二维码到达这里
        //手机版通过author.vue到达这里
        [AllowAnonymous]
        [HttpGet]
        [Route("weixin/login")]
        public ActionResult Login(string state, string returnUrl, string type = "userinfo")
        {
            var appId = WxConfig.SenparcWeixinSetting.WeixinAppId;

            if (string.IsNullOrWhiteSpace(returnUrl)) return MessageView("验证失败，请从正规途径进入", returnUrl);
            if (string.IsNullOrWhiteSpace(state)) return MessageView("验证失败，请从正规途径进入", returnUrl);
            HttpContext.Session.SetString("wxState", state); //储存随机数到Session

            var baseUrl = AppConfig.Setting.BaseUrl;
            returnUrl = returnUrl.UrlEncode();
            var url = type == "userinfo"
                ? OAuthApi.GetAuthorizeUrl(appId, $"{baseUrl}/account/weixin/cb/user?returnUrl={returnUrl}", state,
                    OAuthScope.snsapi_userinfo)
                : OAuthApi.GetAuthorizeUrl(appId, $"{baseUrl}/account/weixin/cb/base?returnUrl={returnUrl}", state,
                    OAuthScope.snsapi_base);
            return Redirect(url);
        }

        [AllowAnonymous]
        [HttpGet]
        [Route("weixin/cb/user")]
        public async Task<IActionResult> UserInfoCallback(string code, string state, string returnUrl)
        {
            if (string.IsNullOrEmpty(code)) return MessageView("你拒绝了授权", returnUrl);
            if (state != HttpContext.Session.GetString("wxState")) return MessageView("验证失败，请从正规途径进入", returnUrl);

            OAuthAccessTokenResult result;
            //通过，用code换取access_token
            try
            {
                var appId = WxConfig.SenparcWeixinSetting.WeixinAppId;
                var appSecret = WxConfig.SenparcWeixinSetting.WeixinAppSecret;
                result = await OAuthApi.GetAccessTokenAsync(appId, appSecret, code);
            }
            catch (Exception ex)
            {
                return MessageView(ex.Message, returnUrl);
            }

            if (result.errcode != ReturnCode.请求成功)
                return Redirect($"{returnUrl}?msg={result.errmsg}&t=" + DateTimeHelper.GetUnixDateTime(DateTime.Now));

            try
            {
                var oUser = await OAuthApi.GetUserInfoAsync(result.access_token, result.openid);
                var user = new User
                {
                    city = oUser.city,
                    country = oUser.country,
                    headimgurl = oUser.headimgurl,
                    nick = oUser.nickname,
                    openid = oUser.openid,
                    province = oUser.province,
                    sex = oUser.sex,
                    state = state,
                    unionid = oUser.unionid
                };
                await AppService.User.UpdateUserAsync(user, state);
            }
            catch (Exception ex)
            {
                return MessageView(ex.Message, returnUrl);
            }
            var claims = new[] { new Claim("state", state) };
            var token = GetJwToken(claims, DateTime.Now.AddSeconds(60));
            var url = $"{returnUrl}?code={token}";
            return Redirect(url);
        }

        [AllowAnonymous]
        [HttpGet]
        [Route("weixin/jssdk")]
        public async Task<IActionResult> GetJsSdkUiPackage(string url)
        {
            var appId = WxConfig.SenparcWeixinSetting.WeixinAppId;
            var appSecret = WxConfig.SenparcWeixinSetting.WeixinAppSecret;
            var signPackage = JSSDKHelper.GetJsSdkUiPackage(appId, appSecret, url);
            return OkResult(signPackage);
        }

        [HttpGet]
        [Route("merchant/login")]
        public async Task<IActionResult> Login()
        {
            var state = User.Claims.First(claim => claim.Type == "state").Value;

            var merchant = await AppService.User.GetMerchantByState(state);

            var claims = new[]
            {
                    new Claim("openid", merchant.openid),
                    new Claim("merchant_id", merchant.id.ToString())
                };
            var expires = DateTime.Now.AddDays(60);
            var token = GetJwToken(claims, expires);

            return OkResult(new
            {
                token,
                merchant,
                staticResourcePrefix = "https://cdn.adtaoke.com/static/o2o/merchant/"
            });
        }

        [HttpGet]
        [Route("login")]
        public async Task<IActionResult> UserLogin()
        {
            var state = User.Claims.First(claim => claim.Type == "state").Value;
            var user = await AppService.User.GetUserByStateAsync(state);
            if (user == null) return ErrorResult("state has expired.");
            var openid = user.openid as string;
            var unionid = user.unionid as string;
            if (string.IsNullOrEmpty(unionid)) unionid = "";
            var claims = new[]
            {
                    new Claim("openid",openid  ),
                };
            var expires = DateTime.Now.AddDays(60);
            var token = GetJwToken(claims, expires);
            return OkResult(new
            {
                token,
                user,
                staticResourcePrefix = "https://cdn.adtaoke.com/static/o2o/merchant/"
            });
        }

        [AllowAnonymous]
        [HttpGet]
        [Route("token/test")]
        public IActionResult LoginTest()
        {
            var claims = new[]
            {
                    new Claim("openid", "owWLk0zwmhjHLpeaKu7FkYaRV-mk"),
                    new Claim("merchant_id", "12")
                };
            var expires = DateTime.Now.AddDays(600);
            var token = GetJwToken(claims, expires);
            return OkResult(new
            {
                token,
                expires
            });
        }

        [HttpGet]
        [AllowAnonymous]
        [Route("qrcode/code")]
        public async Task<IActionResult> LoginQrcode(string state)
        {
            var claims = new[] { new Claim("state", "wx_" + state) };
            // var token = await Task.Run(() => GetJwToken(claims,DateTime.Now.AddSeconds(60)));
            var token = await Task.Run(() => GetJwToken(claims, DateTime.Now.AddSeconds(60)));
            return OkResult(new { token });
        }

        [HttpGet]
        [Route("qrcode/status")]
        public async Task<IActionResult> LoginQuery(string type)
        {
            var state = User.Claims.First(claim => claim.Type == "state").Value;
            //101 等待扫码  102 扫码成功, 103 登录成功

            switch (type)
            {
                case "101":

                    state = state.Substring(3);
                    var user = await AppService.User.GetUserByStateAsync(state);
                    if (!string.IsNullOrWhiteSpace(user.openid))
                        return OkResult(new { type = 102, userInfo = user });
                    break;

                case "102":
                    var merchant = await AppService.User.GetMerchantByState(state);
                    if (!string.IsNullOrWhiteSpace(merchant.openid))
                        return OkResult(new { type = 103, merchant });
                    break;

                default:
                    return OkResult(new { type = 101 });
            }

            return OkResult(new { type = 101 });
        }

        [HttpGet]
        [Route("qrcode/merchant")]
        public async Task<IActionResult> LoginQrcodeMerchant()
        {
            var state = User.Claims.First(claim => claim.Type == "state").Value;

            var merchant = await AppService.User.GetMerchantByState(state);
            return OkResult(merchant);
        }

        [HttpGet]
        [Route("qrcode/confirm")]
        public async Task<IActionResult> LoginQrcodeConfirm()
        {
            var state = User.Claims.First(claim => claim.Type == "state").Value;
            await AppService.User.UpdateUserState(state, $"wx_{state}");
            return OkResult(new { code = 1000 });
        }

        [HttpGet]
        public ActionResult MessageView(string message, string returnUrl = "")
        {
            if (!string.IsNullOrWhiteSpace(returnUrl)) return Redirect(returnUrl + "?msg=" + message.UrlEncode());
            return Content(message);
        }

        private static string GetJwToken(IEnumerable<Claim> claims, DateTime expires)
        {
            var tokenKey = AppConfig.Setting.TokenKey;
            var tokenIssuer = AppConfig.Setting.TokenIssuer;

            var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenKey));
            var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

            var jwt = new JwtSecurityToken(
                issuer: tokenIssuer,
                audience: tokenIssuer,
                claims: claims,
                expires: expires,
                signingCredentials: creds);
            return new JwtSecurityTokenHandler().WriteToken(jwt);
        }
    }
}